package com.htgd.gardenexpo.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author ：hejing
 * @date ：Created in 2021/6/2 14:24
 * @description：文件相关操作
 * @modified By：`
 * @version: 1.0
 */
@Slf4j
public class FileUtils {

    private final Map<String, Integer> isMergePart = new HashMap<>();

    /**
     * 下载文件
     * @param response
     * @param file
     * @param newFileName
     */
    public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, String newFileName) {
        try {
            String agent = request.getHeader("USER-AGENT");
            if (null != agent && -1 != agent.indexOf("MSIE") || null != agent && -1 != agent.indexOf("Trident")) {// IE浏览器
                newFileName = java.net.URLEncoder.encode(newFileName, "UTF8");
             } else if (null != agent && -1 != agent.indexOf("Mozilla")) {// 火狐,chrome等浏览器
                newFileName = new String(newFileName.getBytes("UTF-8"), "iso-8859-1");
             }
            response.setHeader("Content-Disposition", "attachment; filename=" + newFileName);
            BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
            InputStream is = new FileInputStream(file.getAbsolutePath());
            BufferedInputStream bis = new BufferedInputStream(is);
            int length = 0;
            byte[] temp = new byte[1 * 1024 * 10];
            while ((length = bis.read(temp)) != -1) {
                bos.write(temp, 0, length);
            }
            bos.flush();
            bis.close();
            bos.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 单文件分片上传
     * 直接将传入的文件分片通过io流形式写入(服务器)指定临时路径下
     * 然后判断是否分片都上传完成，如果所有分片都上传完成的话，就把临时路径下的分片文件通过流形式读入合并并从新写入到(服务器)指定文件路径下
     * 最后删除临时文件和临时文件夹，临时文件夹是通过文件的uuid进行命名的
     * @param filePart 分片文件
     * @param partIndex 当前分片值
     * @param partNum 所有分片数
     * @param fileName 当前文件名称
     * @param fileUid 当前文件uuid
     * @return
     */
    public Boolean singleFilePartUpload(MultipartFile filePart, Integer partIndex, Integer partNum, String fileName, String fileUid) {
        //实际情况下，这些路径都应该是服务器上面存储文件的路径
        String filePath = System.getProperty("user.dir") + "\\file\\";//文件存放路径
        String tempPath = filePath + "temp\\" + fileUid;//临时文件存放路径
        File dir = new File(tempPath);
        if (!dir.exists())  dir.mkdirs();

        //生成一个临时文件名
        String tempFileNamePath = tempPath + "\\" + fileName + "_" + partIndex + ".part";
        try {
            //将分片存储到临时文件夹中
            filePart.transferTo(new File(tempFileNamePath));

            File tempDir = new File(tempPath);
            File[] tempFiles = tempDir.listFiles();

            one: if (partNum.equals(Objects.requireNonNull(tempFiles).length)){
                //需要校验一下,表示已有异步程序正在合并了;如果是分布式这个校验可以加入redis的分布式锁来完成
                if (isMergePart.get(fileUid) != null) {
                    break one;
                }
                isMergePart.put(fileUid,tempFiles.length);
                System.out.println("所有分片上传完成，预计总分片：" + partNum + "; 实际总分片:" + tempFiles.length);
                FileOutputStream fileOutputStream = new FileOutputStream(filePath + fileName);
                //这里如果分片很多的情况下，可以采用多线程来执行
                for (int i = 0; i < partNum; i++) {
                    //读取分片数据，进行分片合并
                    FileInputStream fileInputStream = new FileInputStream(tempPath + "\\" + fileName  + "_" + i + ".part");
                    byte[] buf = new byte[1024*8];//8MB
                    int length;
                    while((length=fileInputStream.read(buf))!=-1) {//读取fis文件输入字节流里面的数据
                        fileOutputStream.write(buf, 0, length);//通过fos文件输出字节流写出去
                    }
                    fileInputStream.close();
                }
                fileOutputStream.flush();
                fileOutputStream.close();

                // 删除临时文件夹里面的分片文件 如果使用流操作且没有关闭输入流，可能导致删除失败
                for (int i = 0; i < partNum; i++) {
                    boolean delete = new File(tempPath + "\\" + fileName  + "_" + i + ".part").delete();
                    File file = new File(tempPath + "\\" + fileName + "_" + i + ".part");
                    System.out.println(i + " ; 是否删除：" + delete + " ; 是否还存在：" + file.exists());
                }
                //在删除对应的临时文件夹
                if (Objects.requireNonNull(tempDir.listFiles()).length == 0){
                    boolean delete = tempDir.delete();
                    System.out.println("文件夹 " + tempPath + "是否删除：" + delete);
                }
                isMergePart.remove(fileUid);
            }

        }catch (Exception e){
            log.error("单文件分片上传失败!",e);
            return false;
        }
        //通过返回成功的分片值，来验证分片是否有丢失
        System.out.println(partIndex.toString());
        return true;
    }

}
